#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/mman.h>
#include "mmap_write_file_range.h"
#include "http_server.h"

#ifndef MAP_FAILED
#define MAP_FAILED ((void *) -1)
#endif /* MAP_FAILED */

int mmap_write_file_range (http_connection_t *http_connection, off_t to)
{
	/* Return values:
	 * -1: Error.
	 *  0: Send has been interrupted.
	 *  1: Range has been sent.
	 */

	void *data;
	ssize_t bytes;
	size_t zero_bytes_sent;

	/* Map file into memory. */
	data = mmap (NULL, http_connection->filesize, PROT_READ, MAP_SHARED, http_connection->fd, 0);
	if (data == MAP_FAILED) {
		return -1;
	}

	zero_bytes_sent = 0;

	do {
		bytes = send (http_connection->sd, (const char *) data + http_connection->offset, to - http_connection->offset + 1, 0);
		if (bytes < 0) {
			if (errno == EAGAIN) {
				munmap (data, http_connection->filesize);
				return 0;
			} else if (errno == EINTR) {
				/* A signal occurred before any data was transmitted. */
				/* Add ourself to the list of interrupted HTTP connections. */
				http_server.interrupted_http_connections[http_server.number_interrupted_http_connections++] = http_connection;

				munmap (data, http_connection->filesize);
				return 0;
			} else {
				munmap (data, http_connection->filesize);
				return -1;
			}
		} else if (bytes == 0) {
			if (++zero_bytes_sent >= 5) {
				munmap (data, http_connection->filesize);
				return -1;
			}
		} else {
			http_connection->last_read_write = http_server.current_time;

			http_connection->sent += bytes;
			http_connection->offset += bytes;

			zero_bytes_sent = 0;

			if (http_connection->offset >= to + 1) {
				/* We are done. */
				munmap (data, http_connection->filesize);
				return 1;
			}
		}
	} while (1);
}
